package com.kelvem.saas.workbanch.core.utils;

import com.kelvem.saas.workbanch.core.exception.SaasException;
import com.kelvem.saas.workbanch.fsm.entity.Entity;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 实体对象的工具类
 */
@Slf4j
public class EntityUtils {

    private EntityUtils() {
        // do nothing
    }

    /**
     * 提取列表的Id
     *
     * @param list 实体列表
     * @param idFieldName id字段名
     * @return List<Long>
     */
    public static <P extends Entity> List<Long> toIds(List<P> list, String idFieldName) {
        List<Long> ret = new ArrayList<>();

        if (list == null || list.isEmpty()) {
            return ret;
        }

        P p = list.get(0);
        Field field = getDeclaredField(p.getClass(), idFieldName);
        if (field == null) {
            return ret;
        }

        try {
            for (Entity po :
                    list) {
                ReflectionUtils.makeAccessible(field);
                Long id = (Long) field.get(po);
                ret.add(id);
            }
        } catch (Exception e) {
            String msg = String.format("EntityUtils:toIds:error, %s", e.getMessage());
            log.error(msg, e);
            throw new SaasException(msg);
        }
        return ret;
    }

    /**
     * 提取列表的Id
     *
     * @param list 实体列表
     * @return List<Long> id列表
     */
    public static <P extends Entity> List<Long> toIds(List<P> list) {

        return toIds(list, "id");
    }

    /**
     * 根据特定字段, 转化List为Map<id, one>
     *
     * @param list 实体列表
     * @param idFieldName id属性名
     * @return List<Long>
     */
    public static <P extends Entity> Map<Long, P> toMapOne(List<P> list, String idFieldName) {

        Map<Long, P> map = new HashMap<>();

        if (list == null || list.isEmpty()) {
            return map;
        }

        P p = list.get(0);
        Field field = getDeclaredField(p.getClass(), idFieldName);
        if (field == null) {
            return map;
        }

        try {
            for (P po :
                    list) {
                Long key = (Long) field.get(po);
                map.put(key, po);
            }
            return map;
        } catch (Exception e) {
            String msg = String.format("EntityUtils:toMapOne:error, %s", e.getMessage());
            log.error(msg, e);
            throw new SaasException(msg);
        }
    }

    /**
     * 根据特定字段, 转化List为Map<id, List<one>>
     *
     * @param list 实体列表
     * @param idFieldName id属性名
     * @return List<Long>
     */
    public static <P extends Entity> Map<Long, List<P>> toMapMany(List<P> list, String idFieldName) {

        Map<Long, List<P>> map = new HashMap<>();

        if (list == null || list.isEmpty()) {
            return map;
        }

        P p = list.get(0);
        Field field = getDeclaredField(p.getClass(), idFieldName);
        if (field == null) {
            return map;
        }

        try {
            for (P po :
                    list) {
                Long key = (Long) field.get(po);

                if (map.containsKey(key)) {
                    map.get(key).add(po);
                } else {
                    List<P> buf = new ArrayList<>();
                    buf.add(po);
                    map.put(key, buf);
                }
            }
            return map;
        } catch (Exception e) {
            String msg = String.format("EntityUtils:toMapMany:error, %s", e.getMessage());
            log.error(msg, e);
            throw new SaasException(msg);
        }
    }

    /**
     * 一对一合并
     *
     * @param srcList          原始实体列表, 引用传递即返回值
     * @param srcIdFieldName   原始Id属性名
     * @param strOneFieldName  原始关联属性名
     * @param mergeList        合并实体列表
     * @param mergeIdFieldName 合并Id属性名
     */
    public static <M extends Entity, N extends Entity> void mergeOne(List<M> srcList, String srcIdFieldName, String strOneFieldName, List<N> mergeList, String mergeIdFieldName) {

        if (srcList == null || srcList.isEmpty()) {
            return;
        }
        if (mergeList == null || mergeList.isEmpty()) {
            return;
        }

        M srcM = srcList.get(0);
        Field srcField = getDeclaredField(srcM.getClass(), srcIdFieldName);
        Field srcOneField = getDeclaredField(srcM.getClass(), strOneFieldName);
        if (srcField == null || srcOneField == null) {
            // todo log warn
            return;
        }

        // list 转化为 map
        Map<Long, N> map = toMapOne(mergeList, mergeIdFieldName);

        try {
            for (M po :
                    srcList) {
                Long id = (Long) srcField.get(po);

                if (map.containsKey(id)) {
                    N buf = map.get(id);

                    ReflectionUtils.setField(srcOneField, po, buf);
                }
            }
        } catch (Exception e) {
            String msg = String.format("EntityUtils:mergeOne:error, %s", e.getMessage());
            log.error(msg, e);
            throw new SaasException(msg);
        }
    }

    /**
     * 一对一合并
     *
     * @param srcList         原始实体列表, 引用传递即返回值
     * @param srcIdFieldName  原始Id属性名
     * @param strOneFieldName 原始关联属性名
     * @param mergeList       合并实体列表
     */
    public static <M extends Entity, N extends Entity> void mergeOne(List<M> srcList, String srcIdFieldName, String strOneFieldName, List<N> mergeList) {

        mergeOne(srcList, srcIdFieldName, strOneFieldName, mergeList, "id");
    }

    /**
     * 一对多合并
     *
     * @param srcList          原始实体列表, 引用传递即返回值
     * @param srcIdFieldName   原始Id属性名
     * @param strOneFieldName  原始关联属性名
     * @param mergeList        合并实体列表
     */
    public static <M extends Entity, N extends Entity> void mergeMany(List<M> srcList, String srcIdFieldName, String strOneFieldName, List<N> mergeList, String mergeIdFieldName) {

        if (srcList == null || srcList.isEmpty()) {
            return;
        }
        if (mergeList == null || mergeList.isEmpty()) {
            return;
        }

        M srcM = srcList.get(0);
        Field srcField = getDeclaredField(srcM.getClass(), srcIdFieldName);
        Field srcOneField = getDeclaredField(srcM.getClass(), strOneFieldName);
        if (srcField == null || srcOneField == null) {
            return;
        }

        try {
            // list 转化为 map
            Map<Long, List<N>> map = toMapMany(mergeList, mergeIdFieldName);

            for (M po :
                    srcList) {
                Long id = (Long) srcField.get(po);

                if (map.containsKey(id)) {
                    List<N> buf = map.get(id);

                    ReflectionUtils.setField(srcOneField, po, buf);
                }
            }
        } catch (Exception e) {
            String msg = String.format("EntityUtils:mergeMany:error, %s", e.getMessage());
            log.error(msg, e);
            throw new SaasException(msg);
        }
    }

    /**
     * 一对一合并 第一个元素
     *
     * @param srcList          原始实体列表, 引用传递即返回值
     * @param srcIdFieldName   原始Id属性名
     * @param strOneFieldName  原始关联属性名
     * @param mergeList        合并实体列表
     */
    protected <M extends Entity, N extends Entity> void mergeTopOne(List<M> srcList, String srcIdFieldName, String strOneFieldName, List<N> mergeList, String mergeIdFieldName) {

        if (srcList == null || srcList.isEmpty()) {
            return;
        }
        if (mergeList == null || mergeList.isEmpty()) {
            return;
        }

        M srcM = srcList.get(0);
        Field srcField = getDeclaredField(srcM.getClass(), srcIdFieldName);
        Field srcOneField = getDeclaredField(srcM.getClass(), strOneFieldName);
        if (srcField == null || srcOneField == null) {
            return;
        }

        try {
            // list 转化为 map
            Map<Long, List<N>> map = toMapMany(mergeList, mergeIdFieldName);

            for (M po :
                    srcList) {
                Long id = (Long) srcField.get(po);

                if (map.containsKey(id)) {
                    List<N> buf = map.get(id);

                    ReflectionUtils.setField(srcOneField, po, buf.get(0));
                }
            }
        } catch (Exception e) {
            String msg = String.format("EntityUtils:mergeMany:error, %s", e.getMessage());
            log.error(msg, e);
            throw new SaasException(msg);
        }
    }

    /**
     * 获取类的属性Field
     *
     * @param clazz     类
     * @param fieldName 属性名
     * @return Field
     */
    public static Field getDeclaredField(Class<?> clazz, String fieldName) {
        //当父类为null的时候说明到达了最上层的父类(Object类).
        while (clazz != null) {
            try {
                Field field = clazz.getDeclaredField(fieldName);
                //field.setAccessible(true); ReflectionUtils替代
                ReflectionUtils.makeAccessible(field);
                return field;
            } catch (Exception e) {
                // do nothing
            }
            clazz = clazz.getSuperclass();
        }
        return null;
    }

}